﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;

namespace Maze
{
    public partial class MazeForm : Form
    {
        public static readonly Size SquareSize = new Size(40, 40);
        
        public const int CoinValue = 10;

        private Timer timer = new Timer();
        private Board board;
        private PathFinder pathFinder;
        private Point lastSquare = new Point(-1, -1);

        public MazeForm(Board board)
        {
            InitializeComponent();
            this.board = board;
            this.pathFinder = new PathFinder(this.board);

            this.MouseClick += new MouseEventHandler(MazeForm_MouseClick);
            this.MouseMove += new MouseEventHandler(MazeForm_MouseMove);
            Player_PlayerScoreChangedEvent(Player.StartScore);
            if (this.board.Player != null)
            {
                this.board.Player.PlayerScoreChangedEvent += new Player.PlayerScoreHandler(Player_PlayerScoreChangedEvent);
            }
        }

        void Player_PlayerScoreChangedEvent(int score)
        {
            this.Text = "Maze, Score: " + score.ToString();
        }

        void MazeForm_MouseMove(object sender, MouseEventArgs e)
        {
            Point square = new Point(e.X / MazeForm.SquareSize.Width, e.Y / MazeForm.SquareSize.Height);

            if ((lastSquare != square) && (this.board[square] != SQ.W))
            {
                this.pathFinder.CurrentPath.Clear();
                this.pathFinder.Find(MazeForm.PixelsToSquare(this.board.Player.Location), square);

                if (!this.timer.Enabled)
                {
                    this.Refresh();
                }
            }
        }

        void MazeForm_MouseClick(object sender, MouseEventArgs e)
        {
            // Convert to square
            if (e.Button == MouseButtons.Left)
            {
                Point square = new Point(e.X / MazeForm.SquareSize.Width, e.Y / MazeForm.SquareSize.Height);

                if (this.board[square] != SQ.W)
                {
                    this.board.Player.GoToSquare(square);

                    if (!this.timer.Enabled)
                    {
                        this.timer.Interval = 50;
                        this.timer.Tick += new EventHandler(timer_Tick);
                        this.timer.Start();
                    }
                }
            }
        }

        void timer_Tick(object sender, EventArgs e)
        {
            this.Render(10);
        }

        protected override void OnPaint(PaintEventArgs e)
        {
            for (int y = 0; y < this.board.Rows; y++)
            {
                for (int x = 0; x < this.board.Columns; x++)
                {
                    Brush brush = null;
                    switch (this.board[x, y])
                    {
                        case SQ.C:
                            // Intentional fallthrough
                        case SQ.S:
                            // Intentional fallthrough
                        case SQ.O:
                            brush = Brushes.LightBlue;
                            break;
                        case SQ.W:
                            brush = Brushes.DarkBlue;
                            break;
                    }

                    Rectangle rectangle = new Rectangle(new Point(x * MazeForm.SquareSize.Width, y * MazeForm.SquareSize.Height), MazeForm.SquareSize);
                    e.Graphics.FillRectangle(brush, rectangle);
                    
                    if (this.board[x, y] != SQ.W)
                    {
                        Point square = new Point(x, y);
                        rectangle.Width--;
                        rectangle.Height--;
                        if (this.board.Player.IncludedInPath(square))
                        {
                            e.Graphics.DrawRectangle(Pens.Pink, rectangle);
                            rectangle.Inflate(-1, -1);
                            e.Graphics.DrawRectangle(Pens.Pink, rectangle);
                        }
                        else
                        {
                            foreach (Point p in this.pathFinder.CurrentPath)
                            {
                                if (p == square)
                                {
                                    e.Graphics.DrawRectangle(Pens.LightGreen, rectangle);
                                    rectangle.Inflate(-1, -1);
                                    e.Graphics.DrawRectangle(Pens.LightGreen, rectangle);
                                    break;
                                }
                            }
                        }
                    }

                    if (this.board[x, y] == SQ.C)
                    {
                        Rectangle coinRectangle = new Rectangle(new Point(x * MazeForm.SquareSize.Width, y * MazeForm.SquareSize.Height), MazeForm.SquareSize);
                        coinRectangle.Inflate(MazeForm.SquareSize.Width / -4, MazeForm.SquareSize.Height / -4);
                        e.Graphics.FillEllipse(Brushes.Yellow, coinRectangle);
                    }
                }
            }

            e.Graphics.FillEllipse(Brushes.Orange, new Rectangle(this.board.Player.Location, MazeForm.SquareSize));
            
            if (this.board.Enemies != null) {
                foreach (Enemy enemy in this.board.Enemies)
                {
                    e.Graphics.FillEllipse(Brushes.GreenYellow, new Rectangle(enemy.Location, MazeForm.SquareSize));
                }
            }
        }

        public void Render(int divider)
        {
            this.board.Player.Update(divider, this.board);

            if (this.HasGameFinished())
            {
                this.ShowEndOfGame("Game Finished. Click to Exit.");
            }
            else
            {
                if (this.board.Enemies != null)
                {
                    foreach (Enemy enemy in this.board.Enemies)
                    {
                        enemy.Update(divider);

                        if (this.board.Player.CollidesWith(enemy))
                        {
                            this.ShowEndOfGame("Game Over. Click to Exit.");
                            break;
                        }
                    }
                }
            }
            
            this.Refresh();
        }

        private void ShowEndOfGame(string text)
        {
            this.timer.Stop();
            Common.TransparentButton endGame = new Common.TransparentButton();
            endGame.Location = new Point(0, 0);
            endGame.Size = this.ClientRectangle.Size;
            endGame.BackColor = Color.Transparent;
            endGame.Click += new EventHandler(endGame_Click);
            endGame.Font = new Font(FontFamily.GenericSansSerif, 16, FontStyle.Bold);
            endGame.Text = text;
            this.Controls.Add(endGame);
            endGame.BringToFront();
        }

        protected override void OnClosing(System.ComponentModel.CancelEventArgs e)
        {
            base.OnClosing(e);
            this.timer.Stop();
        }

        void endGame_Click(object sender, EventArgs e)
        {
            this.Close();
        }

        public void Render()
        {
            this.Render(1);
        }

        public bool HasGameFinished()
        {
            for (int y = 0; y < this.board.Rows; y++)
            {
                for (int x = 0; x < this.board.Columns; x++)
                {
                    if (this.board[x, y] == SQ.C)
                    {
                        return false;
                    }
                }
            }
            return true;
        }

        public static Point PixelsToSquare(Point point)
        {
            return new Point(point.X / MazeForm.SquareSize.Width, point.Y / MazeForm.SquareSize.Height);
        }

        public static Point SquareToPixels(Point square)
        {
            return new Point(square.X * MazeForm.SquareSize.Width, square.Y * MazeForm.SquareSize.Height);
        }

        public int Score
        {
            get
            {
                return this.board.Player.Score;
            }
        }
    }
}
